﻿#region Copyright & licence

// This file is part of Refix.
// 
// Refix is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
// 
// Refix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with Refix.  If not, see <http://www.gnu.org/licenses/>.
// 
// Copyright (C) 2010-11 David Musgrove and others.

#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

using Refix.Core.IO;
using Refix.Core.Options;
using Refix.Core.Properties;
using Refix.Core.Repository;

namespace Refix.Core.Commands
{
    public class Inject : SingleSolutionCommand
    {
        private LocalRepository _localRepository;

        protected override string HelpText
        {
            get { return ResourceDispenser.GetString("INJECT_HELP"); }
        }

        public override bool Execute()
        {
            var solutionFile = new SolutionFile(FileName);
            _localRepository = GetLocalRepository();
            _localRepository.Offline = Options.Options.Any(o => o is Offline);
            if (solutionFile.CachedProjectFiles.Values.Any(projectFile => !(projectFile.CheckDependencyFileExists()
                                                                            && projectFile.CheckBackupFileDoesNotExist())))
            {
                return false;
            }
            string refixFolderPath = Path.Combine(Path.GetDirectoryName(solutionFile.FileName), ".refix");
            var fileCounts = new FileCounts();
            string clrVersion = string.Format("net-{0:0.0}", solutionFile.GetTargetFrameworkVersion());
            foreach (ProjectFile projectFile in solutionFile.CachedProjectFiles.Values)
            {
                ProjectFile dependencyFile = projectFile.GetDependencyFile();
                fileCounts += RewriteProjectFile(projectFile, dependencyFile, refixFolderPath, clrVersion);
                fileCounts.ConfigurationFiles += WriteAssemblyBindingRedirects(projectFile, dependencyFile);
            }
            OutputWriter.WriteLine(ResourceDispenser.GetString("INJECT_PROJECT_FILES_WRITTEN"), fileCounts.ProjectFiles);
            OutputWriter.WriteLine(ResourceDispenser.GetString("INJECT_CONFIG_FILES_WRITTEN"), fileCounts.ConfigurationFiles);
            OutputWriter.WriteLine(ResourceDispenser.GetString("INJECT_FILES_COPIED"), fileCounts.BinaryFiles);

            return true;
        }

        private FileCounts RewriteProjectFile(ProjectFile projectFile, ProjectFile dependencyFile,
                                              string refixFolderPath, string clrVersion)
        {
            var fileCounts = new FileCounts();
            foreach (Reference reference in dependencyFile.References)
            {
                Reference matchingReference =
                    projectFile.References.FirstOrDefault(r => r.AssemblyName == reference.AssemblyName);
                if (matchingReference != null)
                {
                    matchingReference.ResolvedVersion = reference.ResolvedVersion;
                    fileCounts.BinaryFiles += _localRepository.SaveFiles(dependencyFile.AssemblyBindingRedirects,
                                                                        matchingReference.AssemblyName,
                                                                        matchingReference.ResolvedVersion,
                                                                        clrVersion,
                                                                        refixFolderPath);
                }
            }
            fileCounts.ProjectFiles +=
                projectFile.InjectBackupAndSave(dependencyFile.GetAllAssemblyBindingRedirects(projectFile).Count > 0);
            return fileCounts;
        }

        private int WriteAssemblyBindingRedirects(ProjectFile projectFile, ProjectFile dependencyFile)
        {
            bool suppress = Options.Options.Any(o => o is SuppressConfig);
            List<Reference> allAssemblyBindingRedirects = dependencyFile.GetAllAssemblyBindingRedirects(projectFile);
            if (allAssemblyBindingRedirects.Count > 0)
            {
                if (suppress)
                {
                    using (OutputWriter.Highlight(ConsoleColor.Yellow))
                    {
                        OutputWriter.WriteLine(OutputVerbosity.Quiet, ResourceDispenser.GetString("SUPPRESS_HEADER"));
                        OutputWriter.WriteLine(OutputVerbosity.Quiet,
                                           new string('=', ResourceDispenser.GetString("SUPPRESS_HEADER").Length));
                        OutputWriter.WriteLine(OutputVerbosity.Quiet, ResourceDispenser.GetString("SUPPRESS_FILE"),
                                           projectFile.FileName);
                        foreach (var assemblyBindingRedirect in allAssemblyBindingRedirects)
                        {
                            OutputWriter.WriteLine(OutputVerbosity.Quiet, ResourceDispenser.GetString("SUPPRESS_REDIRECT"),
                                               assemblyBindingRedirect.AssemblyName,
                                               assemblyBindingRedirect.SpecifiedVersion,
                                               assemblyBindingRedirect.ResolvedVersion);
                        }
                    }
                }
                else
                {
                    string configPath = Path.Combine(Path.GetDirectoryName(projectFile.FileName),
                        projectFile.IsWebProject() ? "Web.config" : "app.config");
                    ConfigurationFile configurationFile;
                    if (!File.Exists(configPath))
                    {
                        using (File.CreateText(configPath + FileExtensions.BackupFile)) {}
                        configurationFile = ConfigurationFile.CreateNew(configPath);
                    }
                    else
                    {
                        configurationFile = new ConfigurationFile(configPath);
                    }
                    foreach (Reference assemblyBindingRedirect in allAssemblyBindingRedirects)
                    {
                        configurationFile.SafeAddBindingRedirect(assemblyBindingRedirect);
                    }
                    configurationFile.Save();
                }
                return 1;
            }
            return 0;
        }

        #region Nested type: FileCounts

        private struct FileCounts
        {
            public int ProjectFiles { get; set; }
            public int BinaryFiles { get; set; }
            public int ConfigurationFiles { get; set; }

            static public FileCounts operator +(FileCounts left, FileCounts right)
            {
                return new FileCounts
                           {
                               ProjectFiles = left.ProjectFiles + right.ProjectFiles,
                               BinaryFiles = left.BinaryFiles + right.BinaryFiles,
                               ConfigurationFiles = left.ConfigurationFiles + right.ConfigurationFiles
                           };
            }
        }

        #endregion
    }
}